perm filename MBOX.DGL[UP,DOC] blob
sn#487624 filedate 1979-12-06 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00039 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00005 00002 MUSBOX: a high level interpreter/compiler for real-time digital synthesis.
C00006 00003 Abstract:
C00008 00004 Where we begin:
C00014 00005 Using MUSBOX:
C00020 00006 Multiple calls on the same instrument:
C00022 00007 Basic scanner-parser constructs:
C00025 00008 Scanner declarations and special delimiters:
C00032 00009 Predeclared user settable conditions variables:
C00037 00010 Predeclared variables preset by MUSBOX:
C00039 00011 Allocation of processing elements at scan level.
C00041 00012 Reserved procedures in MUSBOX:
C00044 00013 Preloaded variables:
C00045 00014 Symbolic constants:
C00047 00015 Misc. things:
C00050 00016 The scanner-parser:
C00051 00017 Filename handler and conventions:
C00057 00018 Interactive mode:
C00059 00019 Setting timing variables for Samson box output:
C00062 00020 What each instrument is handed by the scanner
C00070 00021 Things available to instruments from MUSBOX.
C00072 00022 BNF of MUSBOX
C00074 00023 New style SAMINS file with sample instruments, modular organization
C00077 00024 ∂ Concept of organization
C00079 00025 ∂ What SAMINS looks like
C00082 00026 ∂ Alternate form for samins
C00083 00027 ∂ What an instrument file looks like
C00085 00028 ∂ SIMP - This instrument is a simple oscillator.
C00088 00029 ∂ MixMod - Multiply two signals by a constant factors between 0 and 8.
C00090 00030 ∂ Summary
C00093 00031 ∂ Loading additional load_modules from SAMINS.
C00096 00032 Putting up the system, conditional compilation and loading:
C00100 00033 That's about all you need to know. If you have any questions,
C00101 00034 Appendix I. Preloading P fields and functions: SegFun and PrePns.
C00105 00035 Appendix II. Getting listings of source code with macros expanded.
C00108 00036 Appendix III. Layout of SAM command files.
C00115 00037
C00118 00038 ∂27-Apr-79 1137 DGL
C00121 00039 ∂16-May-79 1707 DGL NEW XMBOX.REL[SAM,MUS]
C00127 ENDMK
C⊗;
MUSBOX: a high level interpreter/compiler for real-time digital synthesis.
Abstract:
This describes the program MUSBOX, written by Gareth Loy,
which is a sound-score compiler that generates commands for the
Systems Concepts Digital Synthesizer. It is written in the SAIL
programming language and makes extensive use of the parallel process
runtimes available in that language so as to develop a precise and
powerful simulation of the highly pipelined and multiplexed environment
of the Systems Concepts Synthesizer.
At SUAI the system version of this program is SYS:MBOX.
The input language is made to be compatible (insofar as is reasonable)
with the various software sound compilers such as MUSCMP, MUSIC5, etc.
But the facilities available to the sophisticated user go beyond this
by making the entire SAIL language ultimately
accessable to the synthesizer coder.
More documentation is available on various aspects of the Samson box system:
@SUAI:
LRNSAM.DGL[UP,DOC] Operating manual and tutorial
LOWER.TXT[DOC,MUS] Description of command assembler
INTERM.TXT[DOC,MUS] Intermediate level SAIL routines
SAMINS.DOC[INS,MUS] Documentation of available instruments on [INS,MUS]
Where we begin:
The basic operation of this program is as follows:
MUSBOX reads an event list called a PLAY block which is prepared by
the user and which has the role of a musical score. The basic
function of this list is to invoke the execution of special procedures
called instruments. Instruments receive the values passed to them from
the MUSBOX scanner when they are executed, and they interpret the data
passed to them to compile a set of commands to configure the synthesizer.
This stream of commands is written out to the disk. Various ways
exist for passing the disk file to the synthesizer and of hearing the result.
There are two forms of use of this program:
1) novices use a prepared dump of the program available on the system
which is preloaded with a
fixed selection of instruments designed for didactic purposes.
These configure the synthesizer to perform a variety of simple
functions such as sinusoid generation, FM, filtering, reverb, etc.
(For a description of these instruments, see SAMINS.DOC[INS,MUS]. For
a simple description of how to use them, read the page in this
file headed "SYS:MBOX.")
2) Advanced users "roll their own", making their own collection
of instruments from a broader instrument library, or creating their own
instruments. Even there, a large library of basic functions is available
to the designer, which follow standard conventions allowing arbitrary
combinations. (The documentation for other available instruments can also be
found in SAMINS.DOC[INS,MUS]. To create a version of MUSBOX that contains
your menu of instruments, see later in this file under "New Style SAMINS."
No consistent documentation exists that describes how to design
your own instruments. However, there are examples of instruments later
in this file called "Simp" and "MixMod." In addition, the area [INS,MUS]
contains many instruments that can be used as models.)
The design criteria for the construction of this program has been
to make available to the user the broadest possible programming environment
for the configuration of the synthesizer, while trying to make the
inner workings of the program as transparent as possible. There is
a long way to go in the full realization of these criteria. For instance,
the design of instruments is somewhat complicated in comparison
to the equivalent task in the various software sound compilers such as
MUSCMP, NEWMUS, MUSIC5, etc. Several reasons for this are apparent:
For one thing we are controlling a much more complicated process here
than is the case in software compilers. Also, an attempt has been made
to make as much of the total capabilities of SAIL available to the designer
of instruments whereas no known software compiler (and very few other
programming languages) have the flexibility (and hence the complexity) of
this approach. The usage of SAIL in this program touches almost every
component of it including its dynamic core allocation, parallel processing
capabilities, record structure and procedure variables. The low level
routines are hand coded in FAIL, the local assembly language.
The system is sufficiently modular to allow many customized packages.
The problem of designing instruments
is still under development, and great progress is expected in easing the
task of the instrument designer. However it is already about 95% easier than
it was in the beginning and there is every reason to expect it to continue
to improve.
Using MUSBOX:
To run this program type to the monitor:
.R MBOX
This dump of MUSBOX is preloaded with a set of instruments that can be
invoked to generate a file of commands that can be passed to the Synthesizer
to configure it to play something. To get a full description of these
instruments, see SAMINS.DOC[INS,MUS]. The first instrument is described
below as well as there.
The simplest instrument is called SIMP. It is capable of
generating one note where the pitch, amplitude, begin time and duration
are controlled by parameters passed to it in a specific sequence.
Here's an example of an input file: (Reserved words are capitalized throughout
the following.)
PLAY;
simp 0 .5 A 1 F1;
FINISH;
PLAY caused MUSBOX to begin looking for instruments to run.
You say PLAY when you want MUSBOX to start writing out commands, so it opens
an output file. It uses the default output filename of TEST.SAM. If you
wish to call the output file something else, put the name after PLAY (e.g.
PLAY FOO; will cause the file FOO.SAM to be written).
SIMP is an instrument
predefined in the system version of MUSBOX and the numbers and symbols after it
occupy fields that are scanned and passed to SIMP. They are called P fields and
the values they are given determine what SIMP will play.
To refer to a particular P field, attach a number to it:
For instance, P1 is 0, P2 is .5, P3 is A, etc. The meanings of these fields
are relative to what the instrument expects to get, which is defined by
the instrument's design. There are a couple of P fields that have specific
meanings, however: P0 is always an instrument name, in this case SIMP.
P1 is always a begin time in seconds,
P2 is always a duration in seconds. Beyond that, conventional practice
makes P3 be a center frequency (in Hertz) and P4 an amplitude (usually arbitrarily
scaled to go from 0 to 1). Thus in the example, SIMP will start sounding
at 0 seconds after the beginning of the piece, will sound for .5 seconds,
at a pitch of A=440 Hz, at an overall amplitude of 1 (loud).
The next P field is F1 which is a predefined function of time
vs. amplitude which is
appropriate for use as the amplitude envelope of the note. This function
is a piecewise linear description of the instantaneous amplitude through time
of our note. Users can create their own functions, see below.
The FINISH statement forces all instruments not yet
run to do so, prints out some scheduling information having to do with
the running of the instruments, and prompts for more input from the user.
To hear the sound that we have created, if MBOX says
"Input file:" first type the <RETURN> key. MBOX will give you the prompt ">".
Now type <control>P (that is, hold down the <control> key and type P).
MBOX now asks you for number of times to play.
Hit <RETURN> again to play it once. To stop, type <ALT>.
You can create your own functions, called SEG functions. These functions
can be created using either Leland Smith's FUNC program, or
the program SYS:TFUN written by Andy Moorer. (If you
don't know about TFUN, some documentation is available in INTERM.TXT[DOC,MUS],
under the subject of FUNED and EDFUN, there is also some help available
in the program itself, .R TFUN and type HELP.)
You can create longer scores either making them up with the editor,
or with Leland Smith's SCORE program (get LCS to tell you about this). Another
more recent program that is considerably more powerful is Bill Schottstaedt's
PLA program. For information on either of these programs, type
.READ PLA
or
.READ SCORE
to the monitor.
Multiple calls on the same instrument:
Unlike MUSCMP (that is, NEWMUS and MUSIC5 and friends) instruments in MUSBOX
are capable of running more than one copy of themselves at a time.
When MUSBOX runs an instrument, it instantiates a copy of the values
passed to that instrument for each time the instrument is called.
In essence, the code for each instrument is a "template" of the actual
instrument that is run. In this way, if you want an instrument to play
two notes at the same time, the program SPROUTS two copies of this template,
keeping the values passed to the instruments from the PLAY block separate
for each instantiation or sprout of the instrument template.
In short, overlapping the same instrument with itself will work (up to the
limit of the Samson box hardware).
Basic scanner-parser constructs:
A letter is any of the upper or lower case letters A through Z,
plus a few others: "_" and "!" and #. Lower case letters are mapped into
the corresponding upper case letters for purposes of symbol table
comparisons. A digit is any of the characters 0 through 9.
An identifier is a string of characters consisting of a letter
followed by virtually any number of letters and digits. To be explicit,
an identfier must start with a letter, and can then be followed by any
sequence of letters or digits.
Certain characters are called delimiters, they are: space " ",
tab, carriage-return, line-feed, form-feed and comma. Identifiers must be
delimited before and aft by one of these characters. Unlike SAIL, the parent
language of MUSBOX, identifiers need not be delimited by commas in their
declarations (so that VARIABLE FOO,BAR; is the same as VARIABLE FOO BAR;).
There is a set of identifiers which are used as MUSBOX delimiters.
These identifiers are called Reserved Words and may not be used for any
purpose other than those given explicitly in the syntax. Another set of
identifiers have preset declarations. A list of reserved words and
predeclared identifiers may be found in the following pages.
A statement can be an identifier, or an expression, or
a procedure call or an instrument call. Statements are delimited with
a semicolon.
Scanner declarations and special delimiters:
------------------------------------------------------------------------
VARIABLE <identifier> <delimiter> <set of delimited identifiers>;
takes a string of delimited identifiers as argument. The identifiers
so declared can then be used to store values, which are treated as REAL
numbers.
COMMON <identifier> <delimiter> <set of delimited identifiers>;
The first identifier must be a defined and available instrument name,
the following identifier set are then declared as variables which point
to this instrument also.
RECORD <delimited file name identifier set>;
This is used to look up a file with definitions and values of functions
of class SEG and SYNTH written by the TFUN or FUNC programs.
It takes the set of filenames passed as argument and reads in the function
files one at a time. The default extension for these files is ".FUN".
The special scanner-parser used for this is the same exact code as is used
in TFUN, so whatever works there will work here. For instance, currently
TFUN allows names of functions to begin with digits. But calls on such a
function from scan level in MUSBOX will fail because identifiers are defined
to require a letter as their first character. TFUN may someday be made
to be more pickey about what a function can be named.
FUNC <delimited file name identifier set>;
Behaves exactly the same as RECORD, only the name has been changed, use either.
OUTPUT <file name identifier>;
This defines an output filename for the commands that are assembled by
the instruments. The default extension is ".SAM". Multiple OUTPUT statements
will have no effect. The file name will remain the one specified in the
first such statement, until the FINISH statement is scanned, which, among
other things, closes the output file. The next output file specified will
then be opened for subsequent output, and so on.
PLAY <file name identifier>;
begins a PLAY block. A PLAY block is the token PLAY followed by any number
of statements, terminated by FINISH. It is only within a PLAY block that
1) instruments may be called, 2) processing elements can be claimed with
GET, GETDM, GEN_SUM and MOD_SUM.
The PLAY statement may be followed with an optional argument
specifying an output file to the same effect as the OUTPUT declaration.
Again, only the first output filename specification is effective until FINISH
is scanned.
Entering a PLAY block has certain effects.
1) An output file is opened. If no filename has been specified, TEST.SAM
is opened for output.
2) A set of commands called a "preamble" is written into the file.
This contains various things to configure the basic patch of the synthesizer,
including the
number of total ticks (TTIX) and processing ticks (NPTIX) (TTIX and NPTIX
are calculated from SRATE usually, but may be set specifically)
, the number and location of DAC channels (NOUTCHANS and WHICHSIDE),
analog filter settings (FILTERS), and a couple of other things.
Default values for these things are used if they are not set.
Notice that
since this header is written out at the time the first PLAY is scanned, that
means that these things must all be set in advance of the first PLAY.
The settings selected before the first PLAY will remain in force until a
FINISH is seen.
3) Processing elements may now be allocated.
4) Instruments may now be called.
It is legal to say PLAY inside a PLAY block.
This has the effect of resetting the pass counter (PASS) to 0 (see below). In
this way it is possible to concatenate play lists. Only one output file and
one preamble will be written for the first PLAY statement, until FINISH is scanned.
FINISH;
Ends a PLAY block, forces all instruments to quit, closes
the output file. Sampling rate, output files, etc., may now be changed.
INSERT <delimited filename list>;
takes a filename list, suspends
execution of current input file, jumps to the new file and
continues execution there, returning to this level when
that is exhausted. Files can be inserted up to 15 deep.
.FUN files can also be read in this way by adding the
explicit extension.
EXIT;
forces MUSBOX to quit and returns you to the monitor, after
closing all files.
DONT_SCAN;
Stop scanning statements until you see:
SCAN;
which turns scanning back on. This is useful for making
MUSBOX ignore part of the score.
Predeclared user settable conditions variables:
The following variables are used by MUSBOX to determine the status
of various things, which, once set remain constant. For instance, there
are reasons why resetting the sampling rate (SRATE) while processing commands
(that is, inside a PLAY block when instruments are being called) is a
bad idea. So you can set SRATE to anything (or use its default value, whatever)
UNTIL the FIRST PLAY statement. At that point, changing it will have no
effect on the actual sampling rate until the next FINISH;
So, the point at which these variables
become set is when MUSBOX scans the very first PLAY statement. Until then
they can be set, or changed, but after the first PLAY and until the next
FINISH, they remain fixed. No error message is issued if you try to change
any of them inside a PLAY block (although I may change that someday), but
the change will have NO EFFECT until the next PLAY-FINISH block is entered!
------------------------------------------------------------------------
nptix assign to how many processing ticks, defaults to 96;
nutix assign to how many update ticks, defaults to 32;
ttix total of the above two, this determines the sampling rate;
srate will contain sampling rate calculated from nptix and nutix;
mag will have frequency scaling calculated from srate;
pass this is the pass counter, used to determine where we are in
the flow of time. PRINT PASS; tells you where you are in
the command stream. It is a writable variable, but as such
is reserved for wizards.
noutchans assign between 0 and 4 output channels,
defaults to quad;
whichside assign 0 for dacs to read from generator sum memory only,
positive for modifier side only, negative for both sides,
defaults to negative. The following symbolic constants
may be used instead of a numeric argument:
"GEN_SIDE","MOD_SIDE","BOTH_SIDES".
filters for low pass filters on the dacs, assign any of these values:
"unfiltered" ≡ bypass analog filters,
0 ≡ 4.5 kHz
1 ≡ 9.0 kHz
2 ≡ 13.5 kHz
3 ≡ 18.0 kHz,
defaults to 3;
packing set to left_justified, right_justified or full_word,
defaults to full_word which is interpreted by LOWER to
only load full_word when necessary;
optimization set to OPTIMIZE or NON_OPTIMIZE to determine whether
LOWER will pack more commands per word where possible,
defaults to OPTIMIZE;
debug assign with any summation of
debug_instruments to see what values they are passed,
debug_scanning to see what is read in from a file,
debug_arithmetic to see how MUSBOX does it's arithmetic,
debug_stack to see the contents of arithmetic stacks,
debug_scheduling to see how MUSBOX sprouts instruments;
debug_symbols tells about symbol scanner;
boxtyp not used. If you have another synthesizer that you wish to
use MUSBOX on, this can be used to select the appropriate
command assembly routines.
Predeclared variables preset by MUSBOX:
All of the following are set up when you issue a PLAY statement.
They will not be assigned actual values until then! These are writable
variables, so if you want to clobber them you can. No guarantees, however
that doing so will get you anywhere.
------------------------------------------------------------------------
ZERO predeclared sum memory location to always return zero;
That is, since sum memory clears itself on every pass,
if nothing is ever written into a location, that location
will be 0. The location ZERO is reserved by convention,
as the place we never write into (although we could and
boy, would that wreck havoc!) so that we always get 0 from it.
OUTA, OUTB...D predeclared sum memory locations to stuff generator output;
OUTMA, OUTMB..D predeclared sum memory locations to stuff modifier output;
Allocation of processing elements at scan level.
NOTE: These claim and return the address of the next free sum memory
location. They require initialization induced by the PLAY statement, and their
results are invalid outside a PLAY block. Don't try to claim processing
elements outside a PLAY block, the results will be invalid.
------------------------------------------------------------------------
GEN_SUM returns the next free last pass sum memory location;
MOD_SUM likewise for modifiers;
GET calls LOWER routine GET with two arguments, the processing
element id, and the relative number of the processing element.
If the second argument is TRUE then GET returns the lowest
order free processing element. Example: A←Get(id_modifier,63)
claims and returns the address of the 64'th modifier.
GETDM takes length of delay memory desired as argument,
returns base address if successful in claiming memory,
else returns -1. Example A←GetDm(1000) claims 1000 words
of delay memory and returns the base address of the block
in A;
Reserved procedures in MUSBOX:
------------------------------------------------------------------------
ABS returns the absolute value of the argument.
INT returns the integer part of the argument.
LOG returns the log base e of the argument.
SIN returns the sine of the argument.
PRINT prints the subsequent arguments on the user's screen.
PRINT TAKES NO PARENTHESES AROUND THE ARGUMENT LIST.
Example: Print "HI THERE",2*3 b←a, 2*(a+1),↓;
prints HI THERE 6.0 440.0 882.0 <cr>.
NOTE: string constants must be delimited by commas, otherwise
RDNUM returns a real number from the user's terminal;
RDARG like rdnum, it returns a real number from the user's terminal
but if the user types <cr> instead of a number, then the
expression passed to rdarg is returned. For instance,
A←rdarg(3*(foo+1)) will either assign A the number
typed by the user, or, if the user types <cr>, (or if the
user types something that is not a valid real number, like
.oo1 for instance) then it
will assign A the value of 3*(foo+1). Please note, that
the only acceptable input from the user terminal is a
REAL NUMBER, not an expression, but that the argument to
rdarg can be any expression (including calls to other
procedures, etc.);
RAND returns a random number;
WAITER causes the main process (MBOX) to suspend itself until
the time in passes specified by the argument. The main use
of this is to put a silence between subsequent PLAY blocks:
PLAY; SIMP <args>; FINISH; PLAY; WAITER(3*SRATE); FINISH;
PLAY; SIMP <args>; ... will insert 3 seconds of silence
between the first PLAY block and the last one;
Preloaded variables:
These variables are set up when MUSBOX is entered and are valid
anywhere. They are assigned the values indicated below them.
------------------------------------------------------------------------
A, AS, B, C, CS, D, DS, E, F, FS, G, GS,
440,466.16,493.89,261.62,277.18,293.66,311.13,329.63,349.23,369.99,391.99,415.31,
BF, CF, BS, DF, EF, FF, ES, GF, AF
466.16,246.945,523.24,277.18,311.13,329.63,349.23,369.99,415.31
Symbolic constants:
These are predeclared symbols. They contain a value and may not
be overwritten, but the value may be referenced (e.g. a←true, but not true←a).
-------------------------------------------------------------------------
Generators:
g_inactive, g_pause, a_running, b_running, g_wait, c_running,
data_read, data_write, dac_write,
lplusq, lminusq, lexpplus, lexpminus,
sum_of_cosines, sawtooth, square, pulse_train, sine, sin_fm;
Modifiers:
m_inactive, u_noise, tr_u_noise, latch, threshold, invoke_delay_unit,
notwopoles, two_0poles, two_1poles,
notwozeroes, two_0zeroes, two_1zeroes,
int_mixing, one_pole, mixing, one_zero,
four_quad_multiply, am,
maximum, minimum, signum, zero_crossing_pulser,
add_sum_memory, replace_sum_memory;
Delay units:
delay,
d_inactive,
delayline, table_lookup, round_table_lookup;
Debugging:
debug_instruments, debug_arithmetic, debug_scheduling, debug_scanning,
debug_stack,debug_records,debug_template;
Initialization:
samdev,frmdev,
unfiltered,
optimize,non_optimize,
right_justified,left_justified,full_word;
gen_side,mod_side,both_sides.
Misc. commands:
pause_clear,wait_clear;
Constants:
pi,
true,false;
Misc. things:
------------------------------------------------------------------------
COMMENT can be used to enter comments in the text, everything scanned
until the first semicolon is seen will magically disappear;
<, ∂ these are also comment characters and cause MUSBOX to flush the
entire input line. It does not break on a semicolon, but flushes
everything until it sees a <crlf>. SO DON'T PUT ANY STATEMENTS YOU
WANT TO HAVE EXECUTED ON THE SAME LINE AS "<" or "∂";
' When this character is scanned at the beginning of a number
the immediately subsequent value is presumed to be in octal.
↓ This character cause a <cr><lf> to be printed on the user's terminal;
ASK When supplied as an argument anywhere that a filename is expected,
MUSBOX will prompt the user for a filename instead of taking
one from where the ASK statement was found.
See the description of the filename scanner.
(e.g. PLAY ASK; at the head of a PLAY block
will cause the filename handler to prompt the user for
an output file (with .SAM extension implicit). RECORD ASK;
will cause the filename handler to prompt the user for a function
file (with .FUN implicit)).
INSTRUMENTS
is the token referring to the list of loaded instruments. When
used in conjunction with PRINT, it will print the list of those
instruments: PRINT INSTRUMENTS;
FUNCTIONS
is the token for the list of loaded SEG, SYNTH and SSEG functions
e.g.: PRINT FUNCTIONS prints the list;
PNS is the token for the list of last referenced P fields.
PRINT PNS; causes as many P fields as were most recently addressed
by the last instrument scanned. Thirty two P fields are printed
if no instrument has yet been run.
The scanner-parser:
MUSBOX has an inverse Polish expression scanner that works in
the usual fashion.
For instance, in the expression 1 + 2*3↑3, the result will be 55.
The expression (1+2)*3↑(3-1) will be 27.
Precedence of operators is:
[")"][unary negation] (e.g., "-1"; "1-1" is binary negation)
["↑"] exponentiation
["*"]["/"]["⊗"] ("⊗"=left shift, e.g. 1 ⊗ 2 = 4)
["+"]["-"]
["("]["←"] ("←" = assignment statement)
Operators on the same line are of equal precedence.
Multiple operations of the same precedence are done left to right,
e.g., 2+3-4+5 = 6.
Filename handler and conventions:
MUSBOX has one filename handler for all the different kinds
of files it reads. In all the modes that follow, it is possible to
put in any number of filenames and they will be acted on in turn.
Multiple filenames must be delimited with spaces or tabs.
Partial filename specifications are always allowed.
Depending on the context of the filename request, certain
file extensions are defaultable.
There are three levels of input to MUSBOX: FILE PROMPT level,
TTY INPUT level and FILE INPUT level
FILE PROMPT level is what MUSBOX does when it says
"Input file:".
If the <filename> extension is not supplied, MUSBOX looks for a sequence
of files with the following extensions: .HED, .SCR, .PLA, .BOX. If you want
to input a file that has no extension, you must say <filename>.. (that's
right, a period after the filename).
If a filename is found, a lookup is immediately done and command switches
to the contents of that file.
If <alt> is typed, then MUSBOX exits back to the monitor.
If <return> (also called <cr>) is typed, MUSBOX goes to TTY PROMPT level.
FILE input level is when MUSBOX is snarfing down a file that
you pointed it to. This level and TTY level are handled identically,
so whatever you say in a file you can also type in at a TTY and vice
versa (except for Interactive Mode commands, see below).
When the file is exhausted, you are returned to FILE PROMPT level.
TTY INPUT level prompts with a ">". As far as MUSBOX is concerned,
this input mode and FILE INPUT level are identical (except as noted).
To return to FILE PROMPT level, type <alt>. (The next <alt> would exit MUSBOX.)
There are, however some special commands available at TTY level. For
a discussion, see the page on Interactive Mode.
Files can be read in at other than at FILE PROMPT level.
The following commands work for either TTY or FILE INPUT level.
INSERT can be used to switch to another file for further processing
of commands. Execution of the new file begins immediately. More than
one file can be specified, and they are taken in turn. The INSERTed
file can in turn do an INSERT, and these can nest to the level of 15 deep.
It has the same filename extension defaults as FILE PROMPT level.
RECORD input statement accepts the default .FUN extension, otherwise
it behaves exactly like the other input modes. This .FUN extension is the
usual one put on files containing SEG or SYNTH functions.
(Leland C. Smith memorial feature #1) In addition the term FUNC
behaves exactly like RECORD.
The PLAY statement takes an optional argument for the name
of the command output file. Default filename is TEST, default extension
is .SAM.
In addition, until the time that a PLAY statement is scanned, you
can say (Leland C. Smith memorial feature #2) OUTPUT <filename> (defaults
the same as PLAY).
For the filename argument of all of the above (INSERT, RECORD, FUNC,
PLAY and OUTPUT) you can substitute the term ASK. In this case, MUSBOX
will prompt the user for a filename instead, keeping the default extensions
appropriate for the command. For example, a file containing the statement
INSERT ASK; would cause MUSBOX to type out "Insert file: " and wait for the
user to type a filename. Execution would then switch to this file.
NOTE: MUSBOX has a separate scanner for function files (things
with .FUN extension). This scanner is usually invoked by saying
RECORD <filename> <filename>...;
This scanner can be invoked from FILE PROMPT level
by explicitly writing the filename extension of the first file.
For example, these are the same:
>RECORD FOO BAZ BAR;
and
Input File: FOO.FUN BAZ BAR
Interactive mode:
The following commands at TTY level have these effects:
In all versions of MUSBOX:
<filename>αβε Edit the named file. If no filename, then edit
the last file opened by MUSBOX. If no file opened,
then edit the last file ever edited.
<filename>αβP Play the named file through the Samson box.
Asks for # times to play, wait, type <alt> to abort.
This runs Tovar's SAMPLA program as a phantom job to
actually run the synthesizer.
In MUSBOXE (SYS:MBOXE at SUAI)
<function>αβE Edit function with EdFun. Type HELP after entering
EdFun for a list of commands. EdFun is the same routine
that is in SYS:TFUN.
<filename>αβW Write out functions to named file. (TEST.FUN default)
Setting timing variables for Samson box output:
You can now set any of SRATE, MAG, TTIX (total ticks), NPTIX
(number of processing ticks) or NUTIX (number of update ticks) and
the remainder of these variables will be set to reasonable values
(it sez here). Hence, you can say SRATE←25600; and the right things
get done to accomplish that. However, do not try to reset any of
these variables inside a play block, as unpredictable things
will happen.
Additional warnings: The "right thing" may not always be
unambiguous, and MUSBOX does what it can. For instance, simply
setting TTIX does not specify the division between NPTIX and NUTIX,
so this is defaulted to a rather arbitrary ratio between the two: 3/1.
This ambiguity can be overcome by setting NPTIX and NUTIX
yourself, but letting MUSBOX do it should be ok most of the time.
Also, since the thing that really determines the sampling rate
is the integer TTIX, setting SRATE (which is a floating point number)
will instead give you the sampling rate
calculated from the nearest integer value of TTIX. The same goes for MAG.
Everything else is set from this also. So if the SRATE you get is
not exactly the SRATE you set, that's why.
Processing ticks are restricted to the range ≥2 and ≤256.
Update ticks are restricted to be ≥2.
Total ticks are always 8 more than the sum of update and processing
ticks (to account for overhead ticks).
The numbers actually compiled into the SAM command stream
will be slightly different, reflecting how the box likes to think
about these things, that is:
Highest tick will be TTIX-2. This is because the counter must
start at 0, and be one less than the number required (see the Synthesizer
specifications).
Highest proc. tick will be NPTIX-1 also because counter must start
at 0, again as per the spec. sheet.
What each instrument is handed by the scanner
A record pointer named Pns is passed to each instrument. It points
to a record which contains all the fields that the scanner
scanned for this instantiation of the instrument. It includes the
following:
Three parallel arrays, one real, one of type record_pointer(any_class)
and one of type string.
These arrays contain all the fields that the scanner found for this instrument
from P0 out to the current maximum of P128. The real array contains
the values parsed from arithmetic expressions, the record_pointer array
contains record pointers to the SEG functions to be used by the instrument
for such things as amplitude and frequency control through time.
The string array is special, and contains the string representation of
the field as it was scanned by MBOX. There are a couple of special things
to mention about the string array. If you don't need to know
these gory details, skip the rest of this paragraph.
1) If a field is made up of more than
one symbol or number, and there are spaces, tabs, etc. between the symbols
or numbers, these are suppressed in the string array. For instance,
if a P field contained the following expression: 1 + 2 * A, then the string array
element will contain a packed version of it: 1+2*A.
2) Lower case is converted to upper case.
3) If a string constant
is to be passed as a distinct field, it must be delimited by commas. A string
constant is a string with quotes around it. For instance, in the statement:
PRINT "ONE = ",1;, the "ONE = " is a string constant.
What I am saying is, that
if you want to pass something like the string "MUMBLE" to an instrument, like this:
INS 0 1 A 1 F1 "MUMBLE", OUTA;, then "MUMBLE" must have a comma after it.
(Why? I'm glad you asked! It's because, just like in SAIL, undelimited
strings can precede ANY token. For a SAIL example: BEGIN "FOO" INTEGER A;...
The "FOO" is an undelimited string constant, (also the name of the block).
The undelimited string constant must be suppressed, and is, and it's the
same in MBOX. The statement "MUMBLE" VARIABLE BAZ; is treated exactly
the same as the statement VARIABLE BAZ. However "MUMBLE",VARIABLE BAZ;
is taken as two separate fields. Got it?)
One last detail about string constants: EVERYTHING inside the quotes is
passed just as it is seen. So if you were passing the string constant:
"1 + 2 * A", then the instrument would get just exactly that string without
any compaction or anything.
Here's an example of a PLAY list:
if the scanner saw a PLAY list that looked exactly like this:
PLAY;
SIMP 0 1 A amp1 1 + 2 + 3 OUTA,"mumble"," foo ";
FINISH;
(where SIMP is the name of an instrument, A is a predeclared variable
that returns the value 440,
OUTA is a predeclared variable
containing the addresses of a sum memory location,
and finally, AMP1 is a SEG function that has been read in from a file),
then the fields presented to the instrument
on the three parallel arrays will be as follows:
array element number:
0 1 2 3 4 5 6 7 8
value in string array:
"SIMP" "0" "1" "A" "AMP1" "1+2+3" "OUTA" "MUMBLE" " FOO " (←note the spaces around foo!)
value in real array:
debug 0. 1. 440. 0. 6. 384. 0 0
value in record_pointer array:
SIMP nr nr nr AMP1 nr nr nr nr
We see that only one SEG record was passed to this instrument in P4
(nr is used here to mean "null_record", i.e., no record is being pointed to).
A record_pointer to SIMP shows up in P0 of the record_pointer array.
Remember that instruments are named in P0, so this field points to the instrument
descriptor record. You can find out useful things about the instrument
from this, but I won't go into details.
On the real array, we see the result of 1+2+3 and the address of
sum memory location OUTA (384).
In the real array, the meaning of debug is this: since P0 is
reserved for the record_pointer to the instrument linked list, no number
can occupy P0. So instead, a debug flag is placed there which the instrument
can interpret. When a user says something like
DEBUG←DEBUG_INSTRUMENTS;
to the scanner, the scanner turns on a certain bit in the debug flag corresponding
to the value represented by the symbolic constant DEBUG_INSTRUMENTS.
This flag is copied into the real array when the instrument is called.
An instrument can subsequently test to see if this bit is on, and if it is,
can then do various things, such as print out to a teletype the values
it was passed so the user can see if they agree with what he thinks should
be there. For an example of this, see the sample instruments Simp and MixMod
later in this file.
Things available to instruments from MUSBOX.
external procedure BoxError(string errmsg);
Invokes MBOX's error handler with your message.
external boolean procedure getRecord(string name;
reference record_pointer(any_class) r;
record_pointer(any_class) Tops);
Searches a linked list for a record name matching the string name.
Searches the list pointed to by Top, returns the pointer in r.
external procedure LinkUp(
reference record_pointer(any_class) lstTop;
Record_Pointer(any_class) lstTmpTop;
integer lstTyp(functionList));
This is used to link the record named in lstTmpTop
to the list named in lstTop.
external record_pointer(InsCls) insTop;
Points to the top of the instrument linked list.
external record_pointer(InsCls) funTop;
Points to the top of the function linked list.
External real srate,mag;
Has the value of the sampling rate and mag.
BNF of MUSBOX
Take this with a grain of salt...
<play_block> ::= PLAY; <play_list> |
PLAY <filename>; <play_list>
<play_list> ::= <instrument_call>;<play_list>|
<statement_list>;<play_list>|
FINISH
<statement_list>::= <statement_list><statement>
<statement> ::=<null> | <reserved_word> | <field_list> |
<instrument_call> | <procedure_call>
<instrument_call>::= <instrument_identifier> |
<instrument_identifier><field_list>
<procedure_call>::= <procedure_identifier> |
<procedure_identifier> ( <field_list> )
<field_list> ::= <field> | <field_list><field_mark><field>
<field> ::= <expression> | <record_pointer> | <instrument_identifier> |
<procedure_call> | <delimited_string_constant>
<field_mark> ::= <space> | <comma> | <tab> | <cr> | <ff> | <lf>
New style SAMINS file with sample instruments, modular organization;
∂ This is the example file for the modular approach to instrument
design. See the last page of this section for a summary;
∂ This description is oriented towards users who need to convert from the
old style format. If you don't know what that was, you're better off,
so read on and don't worry about it.
∂ Where the time savings comes from:
∂ A special dump of the SAIL compiler called INSCOM is used to compile the
instruments which has it's symbol tables preloaded with SAMLIB.HDR, the code that
used to be needed on page 2 of SAMINS and lots of other useful stuff;
The time it takes to load SAIL's symbol table constitutes most of the time
it takes to compile an instrument.
∂ The instruments are put into separate files of their own, and compiled
separately from eachother. This means that you never need to recompile an
instrument that already works just because it's in a file with another instrument
that does not. Any collection of these instruments can be assembled even
though they are in separate files;
∂ I estimate that I've saved about 90% of the time it used to take to recompile
instrument files, and having only one copy of the source code for the instrument
saves lots of disk space;
∂ Concept of organization;
∂ Basicly it's simple. A SAMINS file used to contain three categories
of code:
1) Code on page 2 contained macros and requires for the following instruments.
2) The instruments.
3) A procedure (usually called something like MakeInsList) that called
LnkIns or LnkInsP.
The new format simply rearranges this to be more flexible, smaller chunks;
∂ Each instrument is now in a file of its own. Each instrument has a companion
procedure to link it up to MBOX. (Many instruments can be in one file, but
each still has an individual link procedure so they can be split easily);
∂ There is still a SAMINS file, but it's only function is to tell MBOX which
instruments to load;
∂ What SAMINS looks like;
∂ The code that used to be on page 2 is already inside INSCOM, so is not needed;
∂ There is no BEGIN "INSTS" statement here because it has been put inside
INSCOM also;
∂ All the declarations in SAMLIB.HDR[SUB,SYS] as well as
all the macros in SAMINS.HDR[SAM,MUS] are already
available to be used here, including LnkIns, Parameters, InsLnk, etc.;
∂ However,
the END "INSTS" statement must be at the end of all code in SAMINS.SAI
In addition, it must be at the end of every separate file that you want
INSCOM to compile, i.e., all your instruments as well. If you want to have
more than one instrument in a file, then put the end "INSTS"; at the end
of the last instrument;
∂ Since each instrument has its own procedure to link it to MBOX, SAMINS does
not need to, so its only job is to require the .REL files that contain
the compiled instruments;
∂ Here is the only unfortunate aspect of this whole mess:
To use INSCOM you must have a small file,
INSCOM.REL[SAM,MUS] in your directory (it's 380 some words long). This
file is copied out by INSCOM and expanded upon to become SAMINS.REL which you
load with MBOX. Say:
.COPY INSCOM.REL[SAM,MUS] to get this file on your area;
∂ What follows is a complete SAMINS.SAI file to load instruments SIMP and
MIXMOD. Code for these two instruments can be found on the next pages,
but would presumably be in separate files of their own;
∂ Here begins SAMINS.SAI;
require "SIMP.REL[SAM,DGL]" load_module;
require "MIXMOD.REL[SAM,DGL]" load_module;
end "INSTS";
∂ Here ends SAMINS.SAI;
∂ It is necessary for this END statement to be here, see above;
∂ It is also necessary that "INSTS" be CAPITALIZED!;
∂ This is the end of SAMINS.SAI;
∂ Alternate form for samins;
∂ if all the instrument .REL files are on one area from which MBOX will
be loaded, then the following predefined macro can be used;
∂ The source code of this macro is in SAMINS.HDR[SAM,MUS];
∂ Here begins SAMINS:
Instruments(<(simp,mixmod)>);
end "INSTS";
∂ Here ends SAMINS:
∂ and that's all there is to it!;
∂ What an instrument file looks like;
∂ Again, none of the stuff that used to be on page 2 is required;
∂ No BEGIN "INSTS" is needed, but END "INSTS" is necessary;
∂ In outline form, here is a simple instrument:
(file begins here)
Internal recursive procedure InstrumentName(parameters);
begin "InstrumentName"
.
.
.
end "InstrumentName";
InsLnk(<InstrumentName,HowManyParameters>);
end "INSTS";
(file ends here)
∂ Essential differences to old instrument are:
1) Must add INTERNAL to the type of the procedure.
2) Each instrument must call InsLnk to link it to MBOX;
∂ InsLnk takes the following forms:
1) InsLnk(<InstrumentName,HowManyParameters>)
2) InsLnk(<InstrumentName>).
In case (2) the instrument will get 32 parameters by default.
∂ More than one instrument can be in a file if you wish, but each one
must be followed by its own call to InsLnk;
∂ SIMP - This instrument is a simple oscillator.;
∂ Parameters:
P1 P2 P3 P4 P5 P6
Beg Dur Freq Amp Ampfun outloc
;
Internal recursive procedure Simp(Parameters);
begin
real Freq,Amp;
integer outloc, Beg, Dur;
record_pointer(seg)AmpFun; ∂ For storing locations of function;
itemvar AmpItem;
integer Gena,DFlag;
resetP(0); ∂ resetP, incP and pCtr are three macros that simplify ;
DFlag ← Pn[incP]; ∂ picking up parameters. ResetP resets the value of pCtr ;
Beg ← Pn[incP]; ∂ to the value of its argument (in this case 0). IncP ;
Dur ← Pn[incP]; ∂ returns the value of pCtr, then increases its value by 1.;
Freq ← Pn[incP]; ∂ This allows the quantity and order of parameters to be ;
Amp ← Pn[incP]; ∂ reshuffled with impunity. Also, pCtr-1 is the highest ;
AmpFun ← Pf[incP]; ∂ parameter referenced by the instrument, and can be passed;
outloc ← Pn[incP]; ∂ to InsLnk. They are defined in SAMINS.HDR[SAM,MUS];
deleteArray; ∂ Deletes parameter array;
if AmpFun=null_record then BoxError("SIMP: No amp. function!");
if Decode(outLoc)≠id_sum_memory then outLoc←outa;
if DFlag land debug_instruments then
PRINT(↓,"SIMP ",cvn(myproc)," P1 = ",Beg/srate," P2 = ",Dur/srate,↓);
Gena ← GOSC(Beg, Dur, Freq,
AmpFun, amp, 0, AmpItem,
SineMode, 0, zero, outloc);
Join({AmpItem});
RelPe(Gena);
RelItm(myproc);
end;
InsLnk(<simp,pCtr-1>); ∂ Links this instrument to MBOX;
END "INSTS"; ∂ Must be last thing in instrument file;
∂ MixMod - Multiply two signals by a constant factors between 0 and 8.
∂ sample call:
MixMod beg dur factor1 factor2 in1loc in2loc outloc;
Internal recursive procedure MIXMOD(Record_Pointer(InsArrRec) Pns);
begin
real factor1, factor2;
integer in1Loc, in2Loc, outLoc, beg, dur;
integer mod, debIns;
resetP(0);
debIns ←Pn[incP];
beg ←Pn[incP];
dur ←Pn[incP];
factor1 ←Pn[incP];
factor2 ←Pn[incP];
in1Loc ←Pn[incP];
in2Loc ←Pn[incP];
outLoc ←Pn[incP];
$RECFN(5,Pns); ∂ delete global Pns array record pointer;
if debIns=debug_instruments
then
print("MIXMOD",cvn(myproc), " P1= ",beg/srate, "P2= ",dur/srate,↓,
tab, factor1, factor2, sp, in1Loc, sp, in2Loc,sp, outLoc,↓);
mod ← MIX(outLoc,in1Loc,factor1,
in2Loc,factor2);
Waiter(beg+dur);
bind(mod,mode,m_inactive);
RelPe(mod);
if debIns=debug_instruments
then
print("MIXMOD: ",cvn(myproc), " P1= ",beg/srate, "P2= ", dur/srate,
" Dropping off end",↓);
RelItm(myproc);
end;
InsLnk(<mixmod,pCtr-1>);
end "INSTS";
∂ end of MIXMOD;
∂ Summary;
Advantages to modular approach;
1) no need for more than one copy of the source code for an instrument.
2) eliminates needles recompilation of instruments that work to debug
ones that don't.
3) Saves about 90% compile time in comparison to running the straight
SAIL compiler because the symbol tables are already loaded.
A) INSTRUMENTS:
1) make each instrument a separate file.
2) forget about the formerly everpresent, ubiquitous page 2 code.
3) end the instrument with end "INSTS"; (CAPITALIZED!)
4) there is no corresponding begin "INSTS", that's inside INSCOM.
5) R INSCOM for each instrument file to be compiled.
6) Say <FILENAME> ← <FILENAME> to INSCOM. It will read
<FILENAME>.SAI and write <FILENAME>.REL.
My method is to make the .SAI filename the same as the .REL one.
Also, I make the instrument name the same as the filename, just for convenience.
7) when you have done this for every instrument you want to load then
make up a SAMINS file that will load them all together.
B) SAMINS:
1) require the instrument files as load_modules.
2) end all your instrument files with end "INSTS" (CAPITALIZED).
3) R INSCOM and say SAMINS←SAMINS. Same thing happens as with
instruments.
C) Now, load MBOX as you ordinarily would:
.LOAD MBOX[SAM,MUS]/SAI/REL/SAV
That's it!
D) Now, when you need to recompile an instrument, you only need compile
that instrument (using INSCOM), then load MBOX again. If you want to change
the instruments which are loaded, you must change SAMINS,
compile it (with INSCOM) and reload MBOX.
∂ Loading additional load_modules from SAMINS.
Please note, that because of the order in which load_modules are required
in MBOX, there is the facility that you can have your instrument file, SAMINS.REL
require any load_modules, and those will be found and loaded by the LOADER
before SAMLIB is looked up. This means, for instance, that if you were
debugging a piece of code from SAMLIB, you could have a same-named procedure
on your own area, required in your SAMINS file, and when the LOADER is doing
it's thing it will load your piece of code and not the one from SAMLIB, simply
because it loads the first thing it finds, and subsequent same-named pieces
of code are ignored.
(Note, this is not the same problem as having two Internal or External
declarations in your source code. That is a multiply defined symbol. But
having two .REL files with the same procedure is ok, the LOADER just finds
the first one and ignores the second.)
NOTE: For some reason, the LOADER doesn't load LIBRARIES in the
order that they are REQUIRED. If you have a library that you want to load
that will require things from other libraries (like SAMLIB for instance)
it is necessary to explicitly run the loader on that file first. For example:
.LOA MYLIB,MBOX
will successfully load MYLIB even if requiring it as a LIBRARY does not.
;
Putting up the system, conditional compilation and loading:
The current version of MUSBOX is always kept as MBOX.SAI[SAM,MUS].
There are conditional compilation switches documented on page 2 of that
file which select whether the version will have EdFun, whether it will
be able to call SAMPLA, etc. That information is duplicated below.
∂ To compile the standard version of MBOX, comment out XSAMLB and WITH_EDFUN;
∂ To compile version with the function editor EDFUN and SSWRT,
remove the comment from WITH_EDFUN and
tell the system: .COMPILE MBOXE=MBOX, which will create MBOXE.REL;
∂ To compile the version that requires the experimental library,
XSAMLB.REL[SUB,SYS], remove comment from XSAMLB and .COMPILE XMBOX=MBOX
which will create XMBOX.REL;
∂ SAMCAL should be in all versions;
∂ To get your own version of MBOX, after you have made an instrument
file called SAMINS.REL on your area (see MBOX.DGL[UP,DOC] for how
to do this) then type to the monitor
.LOAD MBOX[SAM,MUS]/SAI/SAV/REL;
∂ To put up the system version of MBOX, alias to [INS,MUS] and
.LOAD MBOX[SAM,MUS]/SAI/SAV/REL for the regular version or
.LOAD MBOXE[SAM,MUS]/SAI/SAV/REL for the version with EdFun or
.LOAD MBOX[SAM,MUS]/SAI/SAV/REL for the version that requires XSAMLB;
LOADing MBOX from [INS,MUS] causes MBOX to require SAMINS.REL[INS,MUS]
which contains pointers to the instruments for the system version
When the LOADER is done, .ALIAS 1,3 (which is where system programs
are kept) and .RENAME MBOX.DMP[INS,MUS]. Alias back to your area and
it should be possible to .R MBOX (or MBOXE or whatever) and run
the newly created dump;
Following are the various compile time switches for MUSBOX.
They consist of macros, which if are defined, will have the effect
described after their DEFINE. If the DEFINE is commented out, then
that switch will not be set (It does NOT work to change them to FALSE);
COMMENT DEFINE WITH_EDFUN=TRUE; ∂ load with EDFUN and SSWRT;
COMMENT DEFINE XSAMLB=TRUE; ∂ Require XSAMLIB.REL[SUB,SYS];
DEFINE WITH_SAMCAL=TRUE; ∂ enables αβP to run TVR's SAMPHA;
That's about all you need to know. If you have any questions,
I just stepped out...
Appendix I. Preloading P fields and functions: SegFun and PrePns.
------------------------------------------------------------------------
SegFun - This macro can be used to conveniently preload SEG functions
along with your instruments. It takes three arguments:
1) the function name,
2) an optional P field number which, if present, will cause the
defined function to also be assigned to that P field. (If left out,
the function will be defined and available, but just not loaded into
a P field),
3) a list of amplitude,time pairs which look like this:
<(val_1,time_1, val_2,time_2,... val_n,time_n)>.
For example:
SegFun(f1,5,<(0,0, 1,10.5, 0,100.9)>);
The above will create F1, preload it into P5, and it will have
dimensions as in the list. This next one will not be assigned to a P
field, but will be a defined and available function:
SegFun(f2,,<(.1,0, 1,10.5, .6,300.9)>);
Note that there is no limit to the number or values of the
bound pair list. There is also no limit to how many times you may
use SegFun.
A couple of words of WARNING about the use of SegFun:
1) The function name will be coerced to upper case by SegFun. In fact,
all symbols are coerced to upper case by MBOX.
2) If the function is to be loaded into a P field, that P field will
only point to the function until it is written over by some other value
(i.e., an instrument call).
3) Notice that the correct way to not load the function in a P field
is to have the second argument have no number, but the overall call to
SegFun must still have two commas separating the three arguments. See the
second example above.
4) Do not call SegFun from inside an instrument, it must only be called
at the global level. This is because as the SegFun macro expands, it creates
a whole new procedure and arranges for it to be called at SAIL INITIALIZATION
when MBOX is run, so it can't be inside another procedure.
------------------------------------------------------------------------
PrePns - Use this macro to preset P fields with REAL numbers.
It takes a list of numbers, and starts plunking them down starting
at P1 until the list is exhausted. It also puts the string image
of the real number in the parallel string array.
NOTE: call this macro only once! Also, don't try to use it to preload
functions, use SegFun instead to define and assign the function.
Example: Prepns(<(0,.5,440,.5)>);
Appendix II. Getting listings of source code with macros expanded.
You can get a listing of SAIL programs with all the
macros expanded, so that you can see EXACTLY what's going on
in your program. Macros, while a wonderful convenience, are
sometimes a block to comprehension of what is happening
in a piece of code, expanding them turns them back into
the basic SAIL constructs that they represent.
To get a listing of a SAIL program with macros expanded,
for instance of a file called SIMP.SAI[INS,MUS], type to the monitor:
.COMPILE SIMP[SAM,MUS](34F)/CREF
(SAIL compiles SIMP.REL and SIMP.CRF)
.CREF
(a program called CREF is run which reads SIMP.CRF
and produces SIMP.LST)
When CREF is done, you can look at SIMP.LST with E to see
if it is what you want. The first bunch of pages will be
all the various source files required, like maybe SAMLIB.HDR[SUB,SYS]
and Lord knows what else. Finally we get to code for SIMP.
The way it works is, for the compiler switches specified (34f),
it prints the macro name, then puts its expansion between <>.
Lots of other useful stuff comes with this, such as cross references
where every symbol is defined and used, etc., etc...
For details about other features see the SAIL manual, section 22, Using
SAIL, the sub-section on Switches.
-Gareth
To get a listing of your own instrument expanded in the above fashion,
copy SAMINS.SAI←SAMINS.HDR[SAM,MUS]
to your own area; put the code for your instrument (especially including
the END "INSTS"; discussed elsewhere) at the very end of SAMINS.SAI, and
follow the above directions.
-- jms
Appendix III. Layout of SAM command files.
When MBOX writes a command file, there is a set
of standard categories into which the commands fall.
The file can be divided into three parts, the preamble, the user commands,
and the "postamble" (to coin a word!).
A) The preamble.
When MBOX sees the first PLAY statement, it opens the command output
file and writes the preamble out to it. The preamble is itself broken
down into two parts: synthesizer conditioning commands and commands
that set up the DAC channels.
The first group of commands is always issued, but the values in the commands
is set by the user. They are, in order:
1) Number of processing ticks (NPTIX in MBOX, HiProc in DAB).
2) Number of total ticks (TTIX in MBOX, HiTick in DAB).
3) Analog filters (FILTERS in MBOX, TTLa and TTLb in DAB).
4) Clear pass counter, linger for 1 pass. After this command, we know
that the next command will be read on the first update tick of the next pass,
which means we know where we are and what time it is.
The second group in the preamble sets up the DAC channels.
Normally, four generators, four generator sum memory locations, four modifiers
and four modifier locations are configured in the following fashion:
(only one is shown here)
generator side sum memory: OUTA
↓
modifier in mixing mode: moda
↓
modifier side sum memory: OUTMA
↓
generator in DAC mode: gena
↓
to dac on Samson box: DAC
This allows a signal to be picked up either from a modifier or a generator.
Modifiers write into OUTMA, generators write into OUTA, and they are summed
and passed to the DACs. The number of these channels, and whether they
read from both sides, or only generator or modifier side sum memory is
settable by assigning values to NOUTHCANS and WHICHSIDE.
The last commands in the preamble are to clear the pass counter again
so that the user's Linger commands will be relative to the end of the
preamble, not the beginning of it, and the last command is a NOOP, that
is to say, a command that has no effect on the synthesizer, but which has
a special meaning to you and me. The NOOP merely indicates where the
preamble stops and the user commands begin. This bit of information is useful
in two ways, first if you are looking at a disassembly of a command file,
this tells you where the meat of the file begins. Secondly, if you were to
have a program which could merge separately assembled command streams together,
most likely each one would have a preamble, and you'd want to get rid of
one or the other, and this would tell your program where they ended.
The octal value of the NOOP command is '1050000.
The user's commands come next. Finally in the postamble,
there are some commands that cleanup after the user. The presence of these
commands is at the user's discretion, and they are set by assigning
certain options to the MBOX variable PFINISH.
1) The generators and modifiers feeding the DACs are inactivated.
2) Enough NOOP commands (zeros this time, for variety) are issued to completely
fill the 28 word command buffer. This is necessary because of the following
situation: imagine the box had read in the last 28 commands in the command file,
so the SIX stops feeding any more to the box. But the box always wants more
commands, so it complains about being out of commands, even though it still
has 28 valid ones inside. So 28 NOOPs are tacked on the end of the good
commands to make sure all the good stuff gets executed before this would happen.
Lastly, a STOP command is issued. This halts the box. By convention, the
program that is sending the command stream to the box (usually Ken Shoemake's
SAM program) starts scrutinizing the box when it has sent the last buffer
full of commands to the box, to determine when the box has stopped by virtue of
hitting the last command, which is the STOP command. It then knows that
the user's piece is truly over.
∂16-Mar-79 1327 DGL CORE limitations
∂16-Mar-79 0513 PW
To: BIL, DGL
I'm getting burned by a "SPROUT: no core" message while running MBOX. It stops the
program and I can't continue. At the time it blew up, Mbox was running at about
254P, that shouldn't be the problem. Also, the section where it blows works fine
separately, but when I put at the end of a much larger chunk - BOOM! The mix is
end on end so that shouldn't be the problem. Am I exceeding some process allo-
cation somehow? It seems that if I were, it would have blown up much earlier. I
am not using anything new at the time it wedges. What Gives?!?!?
[DGL -Since you are running so close to 256P when it blows up, I'm sure
that you are trying to sprout something that does cross that magic boundary.
The biggest job a user can run is 512P total. SAIL runs with an upper
segment which occupies the address space of the upper 256P, leaving the
actual core available to the SAIL user's program at 256P.
The segment doesn't actually take up that much room, it uses only about
half of it, but to run an upper segment, they had to put the boundary
somewhere, and that seemed like the appropriate place. Obviously, you
don't want to clobber the upper segment, so there's a trap when anything
tries to grow into it.
Two things can be done:
1) reduce the size of the processes you are sprouting using the
SPROUT_DEFAULTS (see the SAIL manual, or ask me more particularly about it,
or PN also knows about it).
2) reload your MBOX so it has no upper segment. This takes everything
from the upper segment that you use and puts it in the lower segment along
with MBOX, and your instruments. This allows you to expand your program
up to the 512P limit. Loading without the upper segment obviously increases
the size of your program, but you still get a lot more room.
-Gareth]
∂27-Apr-79 1137 DGL
∂27-Apr-79 1125 DGL More XMBOX novelty.
To: "@MUSIC.DIS[DOC,MUS]"
Bugs stomped:
1) If MBOX can't enter an Output File (such as if another MBOX is writing
such a file already), it will prompt you for a different one. If you type <return>
it will retry the original filename, <alt> will exit MBOX.
Features added:
1) MBOX now accepts command line filename input.
2) Output file can be specified in filename list using the "←" token.
3) New error modes: "R" to restart MBOX, "B" breeble on errors and "αε".
The first two error modes should be clear.
The third, αε takes a filename as argument and runs the E editor on the named file.
For instance: FOO.PLAαε will go edit that file.
Example of command line filename input:
1)
.RU MBOX;FOO←VIOLIN VLN BAZ<return>
will run MBOX, writing file FOO.SAM and
reading VIOLIN.FUN, VLN.HED and BAZ.SCR (for instance).
2)
.RU MBOX<return>
Input File(s): FOO←VIOLIN VLN BAZ<return>
will do the same thing as (1).
Note: only one output filename term may be encountered for one filename list.
If more than one occurs, unpredictable results will obtain.
That is, don't do this:
.RU MBOX;FOO←BAZ A←Z<return>
but this will work:
.RU MBOX;FOO←BAZ<return>
<...crunch crunch...>
Input File(s); A←Z<return>
INSERT and OUTPUT continue to work the way they did, i.e., only supply input
files to INSERT, and one output file to OUTPUT.
(Hidden feature:
InputFile(s): BAZ←<return>
will open BAZ.SAM, and put you in TTY input mode (the ">" prompt mode).)
Only in XMBOX.REL[SAM,MUS], backup: XMBOX.OLD[SAM,MUS].
-Gareth
∂16-May-79 1707 DGL NEW XMBOX.REL[SAM,MUS]
Some minor bugs fixed and some minor capabilities added. Nonetheless,
explaining them takes some space... Soo...
- Bugs fixed:
-Saying SCAN without DONT_SCAN no longer causes SCAN to be unknown symbol.
-If the FILTERS variable is set to UNFILTERED,
it no longer complains about aliasing if the Nyquist
rate drops below the lowest analog filter.
-Spaces (tabs, etc.) after the last element in a VARIABLE or
COMMON statement no longer causes spurious error message.
-Various bugs flushed out of the filename scanner, it now should work
as advertised:
1) you can enter as many filenames on one line as you like.
The separate files must be separated by spaces or tabs. (All forms of
partial filename specification continue to be supported.)
Action will begin with the first and proceed forthwith until they are all
exhausted.
2) .FUN files are now scanned for automatically wherever a
filename is encountered and are handled appropriately.
3) file lists can be mixed with impunity (.FUN files no longer
need to be segregated).
4) INSERT takes the filenames passed as argument and sticks them
on the front of the list of files not yet acted on. Input is suspended
immediately on the currently open file and switched to the first file
on the newly augmented list.
5) the terms RECORD and FUNC are still available and must be
followed by a filename list of .FUN files only.
-New features:
-New CVOS (convert octal to string) function at scan level. Saying
>PRINT -1, CVOS -1;
will print
-1 777777777777
.
-New scan level boolean variable REALTIME.
If set to FALSE, it unhooks the calculation of SRATE and MAG from
TTIX, NPTIX and NUTIX. Defaults to TRUE, which means changing any
of these variables affects the other. The purpose of this facility
is to be able to calculate frequencies and delay memory lengths, etc.
independently from the actual rate of processing, for instance if it
is going to be done out of real time. Note, even when REALTIME=FALSE,
if the sampling rate or mag is changed,
it is still recalculated to the nearest actual sampling rate the
box is capable of running, which keeps a bit of reality in our time!
Note further that if REALTIME=FALSE, changing any of TTIX, NPTIX or NUTIX will
still cause the other two to be refigured where appropriate (i.e.,
changing TTIX resets NPTIX and NUTIX, changing either NPTIX or NUTIX
changes TTIX).
-New scan level variable PFINISH controls how much of the PFINISH statistics
you see and certain other things when FINISH is parsed.
PFINISH has a set of symbolic constants which are named and behave as follows:
symbol value action
RELDAC '1 - Call RelDac to release dac-related gen's etc.
STOPBOX '2 - Issue a "stop" command to the box as last command
PESTAT '4 - Print processing element statistics
FLUSHCMD '10 - Issue enough no-op's to flush box's command buffer
SCHEDULING '20 - Print scheduling statistics
Default value for PFINISH is '15 (RELDAC+PESTAT+FLUSHCMD).
If anybody takes exception to those names, or has another scheme,
let me know.
Pfinish PESTAT option will also tell how much delay memory was used.
-GetPe should bitch only once when you run out of a certain kind of
processing element. When your requests for that element come back
into range, it resets for the error message.
Putting up new GetPe and so on required new versions of load modules
DM, INTBOX and INTCOR which are now in XSAMLB.
-Gareth